﻿using System;
using System.Collections.ObjectModel;
using System.IO;
using System.Xml.Serialization;
using ICSharpCode.SharpZipLib.Zip;
using tfs = Microsoft.TeamFoundation.WorkItemTracking.Client;

namespace estoque.Model
{
    public class Repository
    {
        private const string FileNameToSaveAndLoad = "repository.xml";
        private static XmlSerializer xmlSer = null;
        private TeamFoundationHandler teamFoundationHandler;

        public Collection<Scenario> Scenarios { get; set; }

        public Repository()
        {
        }

        public Repository(string tfsServer, string tfsProjectName)
        {
            if (!string.IsNullOrWhiteSpace(tfsServer) && string.IsNullOrWhiteSpace(tfsProjectName))
            {
                this.teamFoundationHandler = new TeamFoundationHandler(tfsServer, tfsProjectName);
            }
        }

        public void SaveToXml()
        {
            MakeBackupZipFile();

            using (FileStream fs = File.Create(FileNameToSaveAndLoad))
            {
                if (xmlSer == null)
                    xmlSer = new XmlSerializer(typeof(Repository));
                xmlSer.Serialize(fs, this);
                Tracer.AddDatabaseInformation("File '" + FileNameToSaveAndLoad + "' was saved.");
            }
        }

        public static Repository LoadFromXml()
        {
            try
            {
                using (FileStream fs = File.Open(FileNameToSaveAndLoad, FileMode.Open))
                {
                    if (xmlSer == null)
                        xmlSer = new XmlSerializer(typeof(Repository));
                    object dsObj = xmlSer.Deserialize(fs);

                    Tracer.AddDatabaseInformation("Repository was loaded from " + FileNameToSaveAndLoad);

                    return dsObj as Repository;
                }
            }
            catch
            {
#if DEBUG
                Tracer.AddDatabaseError("File '" + FileNameToSaveAndLoad + "' could not be loaded. Generating testdata.");
                return GenerateTestData(4, 9);
#endif
                Tracer.AddDatabaseError("File '" + FileNameToSaveAndLoad + "' could not be loaded. Generating new repository.");
                return new Repository();
            }
        }

        public void SyncAgainstTFS()
        {
            if (this.teamFoundationHandler != null)
            {
                tfs.WorkItemCollection wiColl = this.teamFoundationHandler.GetAllScenarios();

                foreach (tfs.WorkItem item in wiColl)
                {
                    bool exists = false;
                    foreach (var availableItem in this.Scenarios)
                    {
                        if (availableItem.TfsScenarioId.HasValue && availableItem.TfsScenarioId.Value == item.Id)
                        {
                            exists = true;
                            break;
                        }
                    }
                    if (!exists)
                    {
                        this.Scenarios.Add(new Scenario { Title = item.Title, TfsScenarioId = item.Id, DueDate = item.RevisedDate, IsFinished = false });
                    }
                }

                tfs.WorkItemCollection taskColl = this.teamFoundationHandler.GetAllOpenWorkItems();

                foreach (tfs.WorkItem item in taskColl)
                {
                    bool exists = false;
                    int fittingScenario = -1;
                    foreach (var availableScenario in this.Scenarios)
                    {
                        foreach (var availableTask in availableScenario.Tasks)
                        {
                            if (availableTask.TfsScenarioId.HasValue && availableTask.TfsScenarioId.Value == item.Id)
                            {
                                exists = true;
                                throw new ArgumentOutOfRangeException();
                                // Parent finden
                                //fittingScenario = (int)item.Links[0];
                                break;
                            }
                        }

                        if (exists)
                        {
                            break;
                        }
                    }

                    if (!exists)
                    {
                        foreach (var scenario in this.Scenarios)
                        {
                            if (scenario.TfsScenarioId.HasValue && scenario.TfsScenarioId.Value == fittingScenario)
                            {
                                Task newTask = new Task { DueDate = item.RevisedDate, TfsScenarioId = item.Id, Title = item.Title };
                                scenario.Tasks.Add(newTask);
                                break;
                            }
                        }
                    }
                }
            }
        }

        private static void MakeBackupZipFile()
        {
            if (File.Exists(FileNameToSaveAndLoad))
            {
                string[] filenames = new string[] { FileNameToSaveAndLoad };
                string targetName = DateTime.Now.ToString("yyyyMMdd") + ".backup.zip";

                // Depending on the directory this could be very large and would require more attention
                // in a commercial package.
                using (FileStream target = new FileStream(targetName, FileMode.Create))
                {
                    // 'using' statements guarantee the stream is closed properly which is a big source
                    // of problems otherwise.  Its exception safe as well which is great.
                    using (ZipOutputStream s = new ZipOutputStream(target))
                    {
                        s.SetLevel(9); // 0 - store only to 9 - means best compression

                        byte[] buffer = new byte[4096];

                        foreach (string file in filenames)
                        {
                            // Using GetFileName makes the result compatible with XP
                            // as the resulting path is not absolute.
                            ZipEntry entry = new ZipEntry(Path.GetFileName(file));

                            // Setup the entry data as required.

                            // Crc and size are handled by the library for seakable streams
                            // so no need to do them here.

                            // Could also use the last write time or similar for the file.
                            entry.DateTime = DateTime.Now;
                            s.PutNextEntry(entry);

                            using (FileStream fs = File.OpenRead(file))
                            {
                                // Using a fixed size buffer here makes no noticeable difference for output
                                // but keeps a lid on memory usage.
                                int sourceBytes;
                                do
                                {
                                    sourceBytes = fs.Read(buffer, 0, buffer.Length);
                                    s.Write(buffer, 0, sourceBytes);
                                } while (sourceBytes > 0);
                            }
                        }

                        // Finish/Close arent needed strictly as the using statement does this automatically

                        // Finish is important to ensure trailing information for a Zip file is appended.  Without this
                        // the created file would be invalid.
                        s.Finish();

                        // Close is important to wrap things up and unlock the file.
                        s.Close();
                    }
                }

                Tracer.AddDatabaseInformation("File was backuped to " + targetName);

                System.Threading.Thread.Sleep(1000);
            }
        }

        private static Repository GenerateTestData(int amountOfScenarios, int amountOfTasks)
        {
            Collection<Scenario> list = new Collection<Scenario>();

            Random rnd = new Random();

            for (int j = 0; j < amountOfScenarios; j++)
            {
                Scenario sc = new Scenario { Title = "Scenario " + j.ToString("N0"), IsFinished = false, TfsScenarioId = j, DueDate = DateTime.Now.AddDays(j) };
                for (int i = 0; i < amountOfTasks; i++)
                {
                    Priority prio = (Priority)(rnd.Next(0, 20) / 4);
                    Task task = new Task { Title = "Testeintrag " + i, TaskPriority = prio };

                    if (rnd.Next(0, 5000) > 2000)
                    {
                        task.TfsScenarioId = rnd.Next(2000, 5000);
                    }
                    if (rnd.Next(0, 5) < 2)
                    {
                        task.IsBug = true;
                    }

                    if (rnd.Next(0, 5) < 2)
                    {
                        task.DueDate = new DateTime(2010, 11, rnd.Next(26, 31));
                    }

                    sc.Tasks.Add(task);
                }

                list.Add(sc);
            }

            Repository repository = new Repository();
            repository.Scenarios = list;
            return repository;
        }
    }
}